package org.broad.igv.prefs; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.embed.swing.JFXPanel; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; import javafx.scene.layout.*; import javafx.stage.Stage; import org.broad.igv.DirectoryManager; import org.broad.igv.Globals; import org.broad.igv.ui.IGV; import org.broad.igv.ui.util.FileDialogUtils; import org.broad.igv.ui.util.UIUtilities; import javax.swing.*; import java.awt.*; import java.beans.EventHandler; import java.io.*; import java.util.*; import java.util.List; public class PreferenceEditorFX { public static void main(String[] args) throws IOException { open(null); } public static void open(Frame parent) throws IOException { List<PreferencesManager.PreferenceGroup> preferenceGroups = PreferencesManager.loadPreferenceList(); SwingUtilities.invokeLater(() -> { JDialog frame = new JDialog(parent, "Preferences", true); final JFXPanel fxPanel = new JFXPanel(); Platform.runLater(() -> initFX(frame, fxPanel, preferenceGroups)); frame.add(fxPanel); frame.pack(); frame.setSize(800, 600); frame.setLocationRelativeTo(parent); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); }); } private static void initFX(final JDialog parent, JFXPanel fxPanel, List<PreferencesManager.PreferenceGroup> preferenceGroups) { // final Map<String, String> updatedPrefs = new HashMap<>(); TabPane tabPane = new TabPane(); BorderPane borderPane = new BorderPane(); borderPane.setCenter(tabPane); Scene scene = new Scene(borderPane); Map<String, Map<String, String>> updatedPreferencesMap = new HashMap<>(); for (PreferencesManager.PreferenceGroup entry : preferenceGroups) { if(entry.tabLabel.equals("Hidden")) continue; final IGVPreferences preferences = PreferencesManager.getPreferences(entry.category); final Map<String, String> updatedPrefs = updatedPreferencesMap.containsKey(entry.category) ? updatedPreferencesMap.get(entry.category) : new HashMap<>(); updatedPreferencesMap.put(entry.category, updatedPrefs); final String tabLabel = entry.tabLabel; Tab tab = new Tab(tabLabel); tab.setClosable(false); tabPane.getTabs().add(tab); ScrollPane scrollPane = new ScrollPane(); tab.setContent(scrollPane); VBox vBox = new VBox(); vBox.setFillWidth(true); // vBox.prefWidthProperty().bind(pane.widthProperty()); scrollPane.setContent(vBox); GridPane gridPane = new GridPane(); gridPane.setHgap(5); gridPane.setVgap(5); vBox.getChildren().add(gridPane); String currentGroup = null; int row = 1; for (PreferencesManager.Preference pref : entry.preferences) { try { Tooltip tooltip = pref.getComment() == null ? null : new Tooltip(pref.getComment()); if (pref.getKey().equals(PreferencesManager.SEPARATOR_KEY)) { Separator sep = new Separator(); GridPane.setColumnSpan(sep, 4); gridPane.add(sep, 1, row); row++; continue; } if (pref.getKey().equals(PreferencesManager.INFO_KEY)) { row++; Label label = new Label(pref.getLabel()); label.setStyle("-fx-font-size:16"); label.setStyle("-fx-font-weight: bold"); GridPane.setColumnSpan(label, 4); gridPane.add(label, 1, row); row += 2; continue; } if (pref.group != null && !pref.group.equals(currentGroup)) { // Start a new group row = 0; currentGroup = pref.group; gridPane = new GridPane(); gridPane.setHgap(5); gridPane.setVgap(5); vBox.getChildren().add(gridPane); TitledPane tp = new TitledPane(currentGroup, gridPane); tp.setCollapsible(false); vBox.getChildren().add(tp); vBox.setMargin(tp, new Insets(10, 0, 0, 0)); } if (pref.getType().equals("boolean")) { CheckBox cb = new CheckBox(pref.getLabel()); cb.setSelected(preferences.getAsBoolean(pref.getKey())); cb.setOnAction(event -> { updatedPrefs.put(pref.getKey(), Boolean.toString(cb.isSelected())); System.out.println("Set " + pref.getLabel() + ": " + cb.isSelected()); }); GridPane.setColumnSpan(cb, 2); gridPane.add(cb, 1, row); if (tooltip != null) { cb.setTooltip(tooltip); } } else if (pref.getType().startsWith("select")) { Label label = new Label(pref.getLabel()); String[] selections = Globals.whitespacePattern.split(pref.getType())[1].split("\\|"); final ComboBox comboBox = new ComboBox(FXCollections.observableArrayList(Arrays.asList(selections))); comboBox.valueProperty().setValue(pref.getDefaultValue()); comboBox.valueProperty().addListener((ov, t, t1) -> { System.out.println("Set " + pref.getLabel() + " " + comboBox.valueProperty().toString()); }); gridPane.add(label, 1, row); GridPane.setColumnSpan(comboBox, 3); gridPane.add(comboBox, 2, row); if (tooltip != null) { label.setTooltip(tooltip); comboBox.setTooltip(tooltip); } } else { Label label = new Label(pref.getLabel()); TextField field = new TextField(preferences.get(pref.getKey())); field.setPrefWidth(500); field.setOnAction(event -> { final String text = field.getText(); if (validate(text, pref.getType())) { updatedPrefs.put(pref.getKey(), text); } else { field.setText(preferences.get(pref.getKey())); } }); field.focusedProperty().addListener((observable, oldValue, newValue) -> { if (newValue == false) { final String text = field.getText(); if (validate(text, pref.getType())) { updatedPrefs.put(pref.getKey(), text); } else { field.setText(preferences.get(pref.getKey())); } } }); gridPane.add(label, 1, row); gridPane.add(field, 2, row); if (tooltip != null) { label.setTooltip(tooltip); field.setTooltip(tooltip); } } row++; } catch (Exception e) { e.printStackTrace(); } } if (tabLabel.equalsIgnoreCase("Advanced")) { // Add IGV directory management at the end. This is a special case String currentDirectory = DirectoryManager.getIgvDirectory().getAbsolutePath(); final Label currentDirectoryLabel = new Label("IGV Directory: " + currentDirectory); final Button moveButton = new Button("Move..."); row++; gridPane.add(currentDirectoryLabel, 1, row); GridPane.setHalignment(moveButton, HPos.RIGHT); gridPane.add(moveButton, 2, row); moveButton.setOnAction(event -> { // Do this on the Swing thread until we port to javafx file dialog UIUtilities.invokeOnEventThread(() -> { final File igvDirectory = DirectoryManager.getIgvDirectory(); final File newDirectory = FileDialogUtils.chooseDirectory("Select IGV directory", DirectoryManager.getUserDirectory()); if (newDirectory != null && !newDirectory.equals(igvDirectory)) { DirectoryManager.moveIGVDirectory(newDirectory); Platform.runLater(() -> currentDirectoryLabel.setText(newDirectory.getAbsolutePath())); } }); }); } } HBox hbox = new HBox(); hbox.setAlignment(Pos.CENTER_RIGHT); hbox.setPadding(new Insets(15, 12, 15, 12)); hbox.setSpacing(5); hbox.setStyle("-fx-background-color: #336699;"); Button cancelBUtton = new Button("Cancel"); cancelBUtton.setPrefSize(100, 20); cancelBUtton.setOnAction((event) -> { SwingUtilities.invokeLater(() -> parent.setVisible(false)); }); Button saveButton = new Button("Save"); saveButton.setPrefSize(100, 20); saveButton.setDefaultButton(true); saveButton.setOnAction((event) -> { PreferencesManager.updateAll(updatedPreferencesMap); SwingUtilities.invokeLater(() -> parent.setVisible(false)); if(IGV.hasInstance()) { IGV.getInstance().doRefresh(); } }); hbox.getChildren().addAll(cancelBUtton, saveButton); borderPane.setBottom(hbox); fxPanel.setScene(scene); } private static boolean validate(String text, String type) { if (type.equals("integer")) { try { Integer.parseInt(text); } catch (NumberFormatException e) { return false; } } else if (type.equals("float")) { try { Double.parseDouble(text); } catch (NumberFormatException e) { return false; } } return true; } }